using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using Misuzilla.Crigate.Authenticate;
using Misuzilla.Crigate.Handler;
using Misuzilla.Net.Irc;

namespace Misuzilla.Crigate
{
    /// <summary>
    /// ユーザからの接続を管理するクラス
    /// </summary>
    public class IrcSession : Session
    {
        private Server _server;
        private TcpClient _tcpClient;
        private StreamWriter _writer;
        private TraceListener _traceListener;

        public IrcSession(Server server, TcpClient tcpClient)
        {
            _server = server;
            _tcpClient = tcpClient;
            Authorizer = server.Authorizer ?? new SimpleAuthorizer();
        }

        ~IrcSession()
        {
            Dispose();
        }

        #region プロパティ
        /// <summary>
        /// 認証を実行する機能を取得・設定します。
        /// </summary>
        public IAuthorizer Authorizer
        {
            get; set;
        }
        
        /// <summary>
        /// 接続しているクライアントを取得します
        /// </summary>
        public TcpClient TcpClient
        {
            get { return _tcpClient; }
        }

        #endregion

        /// <summary>
        /// セッションを開始します。
        /// </summary>
        public void Start()
        {
            CheckDisposed();
            try
            {
                User.EndPoint = _tcpClient.Client.RemoteEndPoint as IPEndPoint;
                
                using (NetworkStream stream = _tcpClient.GetStream())
                using (StreamReader sr = new StreamReader(stream, _server.Encoding))
                using (StreamWriter sw = new StreamWriter(stream, _server.Encoding))
                {
                    _writer = sw;

                    String line;
                    while (_tcpClient.Connected && (line = sr.ReadLine()) != null)
                    {
                        try
                        {
                            IrcMessage msg = IrcMessage.CreateMessage(line);
                            OnMessageReceived(msg);
                        }
                        catch (IrcException ircE)
                        {
                            Trace.WriteLine(ircE.ToString());
                        }
                    }
                }
            }
            catch (IOException) { }
            //catch (NullReferenceException) { }
            finally
            {
                Close();
            }
        }

        /// <summary>
        /// セッションを終了します。
        /// </summary>
        public override void Close()
        {
            if (_tcpClient != null && _tcpClient.Connected)
            {
                _tcpClient.Close();
            }
            OnSessionEnded();
            base.Close();
        }

        /// <summary>
        /// ユーザ認証を実行します。
        /// </summary>
        public override Boolean Authenticate()
        {
            try
            {
                if (Authorizer != null)
                {
                    AuthenticateResult authenticateResult = Authorizer.Authenticate(this, User);
                    if (!authenticateResult.IsAuthenticated)
                    {
                        if (authenticateResult.ErrorReply == 0)
                            authenticateResult.ErrorReply = ErrorReply.ERR_PASSWDMISMATCH;
                        if (String.IsNullOrEmpty(authenticateResult.ErrorMessage))
                            authenticateResult.ErrorMessage = "Password Incorrect";

                        SendErrorReply(authenticateResult.ErrorReply, authenticateResult.ErrorMessage);
                    }
                    return authenticateResult.IsAuthenticated;
                }
                else
                {
                    return true;
                }
            }
            finally
            {
                // TODO: SecureString にしたいかも
                User.Password = "";
            }
        }

        public override string ToString()
        {
            return String.Format("Session: User={0}, Client={1}", User.UserName, _tcpClient.Client.RemoteEndPoint);
        }

        #region IRC メッセージメソッド
        /// <summary>
        /// IRCメッセージを送信します
        /// </summary>
        /// <param name="msg"></param>
        public override void Send(IrcMessage msg)
        {
            CheckDisposed();

            if (!OnPreMessageSend(msg)) return;

            // 送信
            lock (_writer)
            {
                try
                {
                    if (_tcpClient != null && _tcpClient.Connected && _writer.BaseStream.CanWrite)
                    {
                        _writer.WriteLine(msg.RawMessage);
                        _writer.Flush();
                    }
                }
                catch (IOException)
                {
                    Close();
                }
            }

            if (!OnPostMessageSend(msg)) return;
        }
        #endregion

        #region IDisposable メンバ
        private Boolean _isDisposed = false;
        /// <summary>
        /// 
        /// </summary>
        private void CheckDisposed()
        {
            if (_isDisposed)
                throw new ObjectDisposedException(GetType().Name);
        }
        public override void Dispose()
        {
            if (!_isDisposed)
            {
                Trace.WriteLine("Session.Dispose");
                
                // TODO: Trace
                Trace.Listeners.Remove(_traceListener);
                if (_tcpClient != null && _tcpClient.Connected)
                {
                    _tcpClient.Close();
                    _tcpClient = null;
                }
                
                base.Dispose();
                _isDisposed = true;
            }
        }
        #endregion
    }
}
